import pickle

from nodes import *
from encoding import *
import leveldb


db = leveldb.LevelDB('./db')


def _get_amount_of_equal_elements(array1, array2):
    for i in range(min(len(array1), len(array2))):
        if array1[i] != array2[i]:
            return i
    return min(len(array1), len(array2))

# Is key_nibbles in db? And return the tuple for authentication.


def presense(db, key_nibbles):
    root_hash = db.Get(b'root_hash')
    curr_node_hash = root_hash
    curr_node = pickle.loads(db.Get(curr_node_hash))
    curr_nibble_index = 0
    visited_hashes = list()
    visited_nodes = list()
    while True:
        visited_hashes.append(curr_node_hash)
        visited_nodes.append(curr_node)
        if type(curr_node) == BranchNode:
            # hash_by_current_nibble = NULL:
            if not curr_node[key_nibbles[curr_nibble_index]]:
                # print('Authentication fails!')
                return []
            # key_nibbles ends, erase the nibble of the branch.
            elif len(key_nibbles) == curr_nibble_index + 1:
                # print('Authentication succeeds!')
                return visited_nodes
            else:  # get down
                curr_node_hash = curr_node[key_nibbles[curr_nibble_index]]
                curr_node_str = db.Get(curr_node_hash)
                curr_node = pickle.loads(curr_node_str)
                curr_nibble_index += 1
        elif type(curr_node) == ExtensionNode:
            node_key = binstr_to_subnib(curr_node.key)
            equal_elems = _get_amount_of_equal_elements(
                node_key, key_nibbles[curr_nibble_index:])
            if equal_elems != len(node_key):
                # print('Authentication fails!')
                return []
            # The extension node is the last node before the leaf node.
            elif node_key == key_nibbles[curr_nibble_index:]:
                # print('Authentication succeeds!')
                return visited_nodes
            else:  # get down
                curr_node_hash = curr_node.child_hash
                curr_node_str = db.Get(curr_node_hash)
                curr_node = pickle.loads(curr_node_str)
                curr_nibble_index += len(node_key)


# Replace KEY_NIBBLES[0] with key_nibbles.
def authentication(root_hash, authen_tuple, key_nibbles):
    curr_nibble_index = 0
    curr_node_hash = root_hash
    for i in range(len(authen_tuple)):
        curr_node = authen_tuple[i]
        if curr_node_hash != binstr(sha256(pickle.dumps(curr_node)).hexdigest()):
            # print('SHA-256 fails!')
            return False
        elif type(curr_node) == BranchNode:
            # hash_by_current_nibble = NULL:
            if not curr_node[key_nibbles[curr_nibble_index]]:
                # print('BranchNode fails!')
                return
            else:
                curr_node_hash = curr_node[key_nibbles[curr_nibble_index]]
                curr_nibble_index += 1
        elif type(curr_node) == ExtensionNode:
            node_key = binstr_to_subnib(curr_node.key)
            if key_nibbles[curr_nibble_index: (curr_nibble_index + len(node_key))] != node_key:
                # print('ExtensionNode fails!')
                return
            else:  # get down
                curr_node_hash = curr_node.child_hash
                curr_nibble_index += len(node_key)
    if curr_nibble_index == len(key_nibbles):
        # print('Authentication succeeds!')
        return True
    else:
        # print('Authentication is not completed!')
        return False


# Test
KEY_NIBBLES = [[0 for col in range(64)] for row in range(10)]

KEY_NIBBLES[0] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6,
                  12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 0, 1, 2, 3, 4, 5, 6, 7]
KEY_NIBBLES[1] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12,
                  7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 1, 10, 11, 12, 13, 0, 1, 2]
KEY_NIBBLES[2] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12,
                  7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 1, 10, 11, 12, 14, 3, 4, 5]
KEY_NIBBLES[3] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12,
                  7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 1, 10, 11, 12, 15, 6, 7, 8]
KEY_NIBBLES[4] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6,
                  12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 3, 4, 5, 6, 7, 8, 0]
KEY_NIBBLES[5] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6,
                  12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 3, 4, 5, 6, 7, 8, 1]
KEY_NIBBLES[6] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6,
                  12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 3, 4, 5, 6, 7, 8, 2]
KEY_NIBBLES[7] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6,
                  12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 3, 4, 5, 6, 7, 8, 3]

KEY_NIBBLES[8] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12,
                  7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 7, 15, 11, 5, 7, 14, 9]
KEY_NIBBLES[9] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12,
                  7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 7, 15, 11, 5, 7, 14, 9]


authen_tuple_0 = presense(db, KEY_NIBBLES[0])

root_hash = db.Get(b'root_hash')

authentication(root_hash, authen_tuple_0, KEY_NIBBLES[0])


'''
### Test
KEY_NIBBLES = [[0 for col in range(64)] for row in range(10)]

KEY_NIBBLES[0] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 0, 1, 2, 3, 4, 5, 6, 7]
KEY_NIBBLES[1] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 1, 10, 11, 12, 13, 0, 1, 2]
KEY_NIBBLES[2] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 1, 10, 11, 12, 14, 3, 4, 5]
KEY_NIBBLES[3] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 1, 10, 11, 12, 15, 6, 7, 8]
KEY_NIBBLES[4] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 3, 4, 5, 6, 7, 8, 0]
KEY_NIBBLES[5] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 3, 4, 5, 6, 7, 8, 1]
KEY_NIBBLES[6] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 3, 4, 5, 6, 7, 8, 2]
KEY_NIBBLES[7] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 3, 4, 5, 6, 7, 8, 3]

KEY_NIBBLES[8] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 7, 15, 11, 5, 7, 14, 9]
KEY_NIBBLES[9] = [5, 15, 0, 12, 14, 11, 6, 6, 15, 15, 12, 8, 6, 15, 3, 8, 13, 9, 5, 2, 7, 8, 6, 12, 6, 13, 6, 9, 6, 12, 7, 9, 12, 2, 13, 11, 12, 2, 3, 9, 13, 13, 4, 14, 9, 1, 11, 4, 6, 7, 2, 9, 13, 7, 3, 10, 2, 7, 15, 11, 5, 7, 14, 9]



LIST0 = presense(db, KEY_NIBBLES[0])


### Replace KEY_NIBBLES[0] with key_nibbles.
curr_nibble_index = 0
curr_node_hash = db.get(b'root_hash')
for i in range(len(LIST0)):
    curr_node = LIST0[i]
    if curr_node_hash != binstr(sha256(pickle.dumps(curr_node)).hexdigest()):
        print('SHA-256 fails!')
        break
    elif type(curr_node) == BranchNode:
        if not curr_node[KEY_NIBBLES[0][curr_nibble_index]]:  # hash_by_current_nibble = NULL:
            print('BranchNode fails!')
            break
        else:
            curr_node_hash = curr_node[KEY_NIBBLES[0][curr_nibble_index]]
            curr_nibble_index += 1
    elif type(curr_node) == ExtensionNode:
        node_key = binstr_to_subnib(curr_node.key)
        if KEY_NIBBLES[0][curr_nibble_index : (curr_nibble_index + len(node_key))] != node_key:
            print('ExtensionNode fails!')
            break
        else:  # get down
            curr_node_hash = curr_node.child_hash
            curr_nibble_index += len(node_key)
    print(curr_nibble_index)
if curr_nibble_index == len(KEY_NIBBLES[0]):
    print('Authentication succeeds!')
else:
    print('Authentication is not completed!')
'''
